package craky.componentc;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;

import javax.swing.ComboBoxEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.BasicListUI;
import javax.swing.plaf.basic.BasicPopupMenuUI;
import javax.swing.plaf.basic.BasicScrollPaneUI;
import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.plaf.basic.ComboPopup;

import sun.swing.DefaultLookup;
import craky.util.UIResourceManager;
import craky.util.UIUtil;

public class CComboBoxUI extends BasicComboBoxUI
{
    private static final Color DISABLED_BG = UIResourceManager.getColor(UIResourceManager.KEY_TEXT_DISABLED_BACKGROUND);
    
    private static final Color NON_EDITABLE_BG = UIResourceManager.getColor(UIResourceManager.KEY_TEXT_NON_EDITABLE_BACKGROUND);
    
    private static final Image RENDERER_BORDER_IMAGE = UIResourceManager.getImage(UIResourceManager.KEY_COMBO_BOX_RENDERER_BORDER_IMAGE);
    
    private static final Image RENDERER_BORDER_DISABLED_IMAGE = UIResourceManager
                    .getImage(UIResourceManager.KEY_COMBO_BOX_RENDERER_BORDER_DISABLED_IMAGE);
    
    protected Image buttonImage, buttonRolloverImage;
    
    public static ComponentUI createUI(JComponent c)
    {
        return new CComboBoxUI();
    }
    
    public void paint(Graphics g, JComponent c)
    {
        hasFocus = comboBox.hasFocus();
        Rectangle r = rectangleForCurrentValue();
        paintCurrentValueBackground(g, r, hasFocus);
        
        if(!comboBox.isEditable())
        {
            paintCurrentValue(g, r, hasFocus);
        }
    }
    
    public void paintCurrentValueBackground(Graphics g, Rectangle bounds, boolean hasFocus)
    {
        if(comboBox instanceof JCComboBox)
        {
            JCComboBox ccombox = (JCComboBox)comboBox;
            float alpha = ccombox.getAlpha();
            Image image = ccombox.getImage();
            boolean imageOnly = ccombox.isImageOnly();
            
            if(alpha > 0.0 && !(imageOnly && image == null))
            {
                UIUtil.paintBackground(g, ccombox, ccombox.isEditableAll()? ccombox.getBackground(): NON_EDITABLE_BG,
                                DISABLED_BG, image, imageOnly, alpha, ccombox.getVisibleInsets());
                
                if(!ccombox.isEnabled() || ccombox.isEditableAll())
                {
                    paintRendererBorder(g, bounds);
                }
            }
        }
        else
        {
            super.paintCurrentValueBackground(g, bounds, hasFocus);
        }
    }
    
    private void paintRendererBorder(Graphics g, Rectangle bounds)
    {
        Image image = comboBox.isEnabled()? RENDERER_BORDER_IMAGE: RENDERER_BORDER_DISABLED_IMAGE;
        Insets imageInsets = new Insets(2, 2, 0, 0);
        Rectangle paintRect = new Rectangle(bounds.x, bounds.y, bounds.width + arrowButton.getWidth(), bounds.height);
        UIUtil.paintImage(g, image, imageInsets, paintRect, comboBox);
    }

    public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus)
    {
        ListCellRenderer renderer = comboBox.getRenderer();
        Component c;

        if(hasFocus && !isPopupVisible(comboBox))
        {
            c = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, true, false);
        }
        else
        {
            c = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, false, false);
        }

        ((JComponent)c).setOpaque(false);
        ((JComponent)c).setBorder(CComboBoxRenderer.SELECTED_BORDER);
        c.setFont(comboBox.getFont());

        if(comboBox instanceof JCComboBox)
        {
            c.setForeground(comboBox.isEnabled()? comboBox.getForeground(): ((JCComboBox)comboBox).getDisabledTextColor());
        }
        else
        {
            c.setForeground(comboBox.isEnabled()? comboBox.getForeground(): DefaultLookup.getColor(comboBox, this,
                            "ComboBox.disabledForeground", null));
        }

        currentValuePane.paintComponent(g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, c instanceof JPanel);
    }
    
    protected JButton createArrowButton()
    {
        JCButton button = new JCButton();
        button.setName("ComboBox.arrowButton");
        button.setImage(buttonImage = UIResourceManager.getImage(UIResourceManager.KEY_COMBO_BOX_BUTTON_IMAGE));
        button.setDisabledImage(button.getImage());
        button.setRolloverImage(buttonRolloverImage = UIResourceManager.getImage(UIResourceManager.KEY_COMBO_BOX_BUTTON_ROLLOVER_IMAGE));
        button.setPressedImage(UIResourceManager.getImage(UIResourceManager.KEY_COMBO_BOX_BUTTON_PRESSED_IMAGE));
        button.setIcon(UIResourceManager.getIcon(UIResourceManager.KEY_COMBO_BOX_BUTTON_ICON));
        button.setDisabledIcon(UIResourceManager.getIcon(UIResourceManager.KEY_COMBO_BOX_BUTTON_DISABLED_ICON));
        button.setImageInsets(1, 2, 1, 1);
        return button;
    }
    
    public void configureArrowButton()
    {
        super.configureArrowButton();
        
        if(arrowButton != null)
        {
            arrowButton.setFocusable(false);
        }
    }

    protected ComboBoxEditor createEditor()
    {
        ComboBoxEditor editor = new CComboBoxEditor();
        JTextField field = (JTextField)editor.getEditorComponent();
        field.setBorder(UIResourceManager.getBorder(UIResourceManager.KEY_COMBO_BOX_EDITOR_BORDER));
        field.setSelectionColor(UIResourceManager.getColor(UIResourceManager.KEY_TEXT_SELECTION_COLOR));
        field.setSelectedTextColor(UIResourceManager.getColor(UIResourceManager.KEY_TEXT_SELECTION_FOREGROUND));
        field.setOpaque(false);
        field.setBackground(UIResourceManager.getWhiteColor());
        field.setForeground(Color.BLACK);
        field.setCaretColor(Color.BLACK);
        field.setMargin(new Insets(0, 0, 0, 0));
        field.setFont(UIUtil.getDefaultFont());
        
        if(comboBox instanceof JCComboBox)
        {
            field.setDisabledTextColor(((JCComboBox)comboBox).getDisabledTextColor());
        }
        
        return editor;
    }

    protected ComboPopup createPopup()
    {
        return new CComboPopup(comboBox);
    }
    
    public ComboPopup getPopup()
    {
        return popup;
    }
    
    public JButton getArrowButton()
    {
        return arrowButton;
    }
    
    public void changeButtonBorder(boolean mouseIn)
    {
        ((JCButton)arrowButton).setImage(mouseIn? buttonRolloverImage: buttonImage);
    }
    
    protected void selectNextPossibleValue()
    {
        if(comboBox instanceof JCComboBox && !((JCComboBox)comboBox).isEditableAll())
        {
            return;
        }
        
        super.selectNextPossibleValue();
    }

    protected void selectPreviousPossibleValue()
    {
        if(comboBox instanceof JCComboBox && !((JCComboBox)comboBox).isEditableAll())
        {
            return;
        }
        
        super.selectPreviousPossibleValue();
    }

    protected void installDefaults()
    {}
    
    protected void uninstallDefaults()
    {}
    
    private class CComboBoxEditor extends BasicComboBoxEditor.UIResource
    {
        protected JTextField createEditorComponent()
        {
            JTextField editor = new BorderlessTextField("",9);
            editor.setBorder(null);
            return editor;
        }
    }
    
    private static class BorderlessTextField extends JTextField
    {
        private static final long serialVersionUID = -330778040758821755L;

        public BorderlessTextField(String value, int n)
        {
            super(value, n);
            setUI(new BasicTextFieldUI());
        }

        public void setText(String s)
        {
            if(getText().equals(s))
            {
                return;
            }
            
            super.setText(s);
            setSelectionStart(0);
            setSelectionEnd(0);
        }

        public void setBorder(Border b)
        {
            if(!(b instanceof BasicComboBoxEditor.UIResource))
            {
                super.setBorder(b);
            }
        }
        
        @Deprecated
        public void updateUI()
        {}
    }

    private class CComboPopup extends BasicComboPopup
    {
        private static final long serialVersionUID = 7897660999228190162L;
        
        private final Border VIEWPORT_BORDER = new EmptyBorder(0, 0, 0, 1);

        private JComboBox combo;
        
        public CComboPopup(JComboBox combo)
        {
            super(combo);
            setUI(new BasicPopupMenuUI()
            {
                public void installDefaults()
                {}

                protected void uninstallDefaults()
                {}
            });
            this.combo = combo;
            setBorder(new EmptyBorder(0, 0, 0, 0));
            setOpaque(false);
        }
        
        protected JScrollPane createScroller()
        {
            JScrollPane sp = new JScrollPane(list, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER)
            {
                private static final long serialVersionUID = -2214894633749404512L;
                
                public JScrollBar createVerticalScrollBar()
                {
                    ScrollBar vBar = new ScrollBar(JScrollBar.VERTICAL)
                    {
                        private static final long serialVersionUID = 397439195886258194L;
                        
                        @Deprecated
                        public void updateUI()
                        {}
                    };

                    vBar.setUI(new CScrollBarUI());
                    vBar.setBorder(null);
                    return vBar;
                }
                
                @Deprecated
                public void updateUI()
                {}
            };
            
            sp.setUI(new BasicScrollPaneUI()
            {
                public void update(Graphics g, JComponent c)
                {
                    g.setColor(c.getBackground());
                    g.fillRect(0, 0, c.getWidth(), c.getHeight());
                    paint(g, c);
                }
                
                protected void installDefaults(JScrollPane scrollpane)
                {}

                protected void uninstallDefaults(JScrollPane scrollpane)
                {}
            });
            sp.setHorizontalScrollBar(null);
            return sp;
        }

        protected void configureScroller()
        {
            super.configureScroller();
            scroller.getVerticalScrollBar().setOpaque(false);
            scroller.getViewport().setOpaque(false);
            scroller.setOpaque(false);
            scroller.setBorder(UIResourceManager.getBorder(UIResourceManager.KEY_COMBO_BOX_POPUP_BORDER));
        }
        
        protected JList createList()
        {
            JList list = new JList(comboBox.getModel())
            {
                private static final long serialVersionUID = -3870496246576971437L;

                public void processMouseEvent(MouseEvent e)
                {
                    if((e.getModifiers() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0)
                    {
                        Toolkit toolkit = Toolkit.getDefaultToolkit();
                        e = new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(), e.getModifiers() ^
                                        toolkit.getMenuShortcutKeyMask(), e.getX(), e.getY(), e.getXOnScreen(),
                                        e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), MouseEvent.NOBUTTON);
                    }
                    
                    super.processMouseEvent(e);
                }

                @Deprecated
                public void updateUI()
                {}
            };

            list.setUI(new BasicListUI()
            {
                protected void installDefaults()
                {
                    list.setLayout(null);
                }

                protected void uninstallDefaults()
                {
                    if(list.getTransferHandler() instanceof UIResource)
                    {
                        list.setTransferHandler(null);
                    }
                }
            });
            return list;
        }
        
        protected void configureList()
        {
            super.configureList();
            list.setOpaque(false);
        }
        
        public void show()
        {
            if(combo instanceof JCComboBox && !((JCComboBox)combo).isEditableAll())
            {
                return;
            }
            
            super.show();
        }

        public void setVisible(boolean visible)
        {
            super.setVisible(visible);

            if(visible)
            {
                if(combo instanceof JCComboBox)
                {
                    JCComboBox cbox = (JCComboBox)combo;
                    float alpha = cbox.isImageOnly()? 0.0f: cbox.getAlpha();
                    Color oldBg = cbox.getBackground();
                    scroller.setBackground(new Color(oldBg.getRed(), oldBg.getGreen(), oldBg.getBlue(), (int)Math.round(255 * alpha)));
                }
                else
                {
                    scroller.setBackground(combo.getBackground());
                }
                
                scroller.setViewportBorder(scroller.getVerticalScrollBar().isVisible()? VIEWPORT_BORDER: null);
            }
            else if(combo instanceof JCComboBox)
            {
                ((JCComboBox)combo).resetBorder();
            }
        }
        
        @Deprecated
        public void updateUI()
        {
            setUI(this.getUI());
        }
    }
}